#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <functional>
#include <pthread.h>
#include "threadpool.hpp"


class TcpServer; //声明
class ThreadData
{
public:
    ThreadData(int skfd, struct sockaddr_in skaddr, TcpServer *self) : _skfd(skfd), _skaddr(skaddr), _self(self)
    {
    }

    int _skfd;
    struct sockaddr_in _skaddr;
    TcpServer *_self;
};

using task_t = function<void()>;

class TcpServer
{
private:
    void server(int sockfd, struct sockaddr_in srcaddr)
    {
        LOG(INFO, "[%s:%d] is linking......\n", inet_ntoa(srcaddr.sin_addr), ntohs(srcaddr.sin_port));
        char buf[1024] = {0};
        while (true)
        {
            // 1、接收数据
            int ret = read(sockfd, buf, sizeof(buf));
            if (ret > 0)
            {
                buf[ret] = '\0';
                LOG(INFO, "[%s:%d]receive:%s\n", inet_ntoa(srcaddr.sin_addr), ntohs(srcaddr.sin_port), buf);

                // 2、发送数据
                write(sockfd, buf, strlen(buf));
            }
            else if (ret == 0)
            {
                // 读到文件结束符返回0，表示client退出或者关闭连接
                LOG(INFO, "[%s:%d]receive:client is quit!\n", inet_ntoa(srcaddr.sin_addr), ntohs(srcaddr.sin_port));
                break;
            }
            else
            {
                // 读到了错误继续读取
                LOG(INFO, "[%s:%d]receive:read is failed!\n", inet_ntoa(srcaddr.sin_addr), ntohs(srcaddr.sin_port));
                continue;
            }
        }
        close(sockfd); // 防止文件描述符泄露
    }

    static void *pthreadServer(void *args) //用于创建线程的回调函数
    {
        ThreadData* td = static_cast<ThreadData*>(args);
        pthread_detach(pthread_self()); //线程分离
        td->_self->server(td->_skfd, td->_skaddr);
        delete td;
        return nullptr;
    }

public:
    TcpServer(uint16_t port) : _port(port), _isrunning(false)
    {
    }

    void initServer()
    {
        // 1、创建套接字
        _listensockfd = socket(AF_INET, SOCK_STREAM, 0); // AF_INET表示ipv4类型网络通信，SOCK_STREAM表示通信协议是tcp
        if (_listensockfd == -1)
        {
            LOG(FATAL, "socket is failed!\n");
            exit(1);
        }
        LOG(DEBUG, "socket is successed!\n");

        // 2、创建套接字网络信息
        struct sockaddr_in localaddr;
        bzero(&localaddr, sizeof(localaddr));
        localaddr.sin_family = AF_INET;
        localaddr.sin_port = htons(_port);
        localaddr.sin_addr.s_addr = INADDR_ANY;

        // 3、绑定
        int ret = bind(_listensockfd, (struct sockaddr *)&localaddr, sizeof(localaddr));
        if (ret == -1)
        {
            LOG(FATAL, "bind is failed!\n");
            exit(1);
        }
        LOG(DEBUG, "bind is successed!\n");

        // 4、tcp是面向连接的，所以通信之前必须先建立连接，而服务器是被连接的，所以要一直等待被连接
        ret = listen(_listensockfd, 16); // 设置套接字为监听状态
        if (ret == -1)
        {
            LOG(FATAL, "listen is failed!\n");
            exit(1);
        }
        LOG(DEBUG, "listen is successed!\n");
    }

    void startServer()
    {
        _isrunning = true;
        while (_isrunning)
        {
            struct sockaddr_in srcaddr;
            socklen_t len = sizeof(srcaddr);
            // 通信之前必须先建立连接
            // 如果连接成功返回套接字文件描述符指针，用于数据的接收和发送
            int sockfd = accept(_listensockfd, (struct sockaddr *)&srcaddr, &len);
            if (sockfd > 0)
            {
                //------version 0------每次只能处理一个用户的连接，当此用户断开连接时，才能循环处理下一个用户
                // server(sockfd, srcaddr);

                // //------version 1------多进程来解决version 0
                // pid_t pid = fork();
                // if(pid == 0)
                // {
                //     //child
                //     //关闭_listensockfd文件描述符，子进程只关心sockfd，不关心_listensockdfd
                //     close(_listensockfd); //建议
                //     int id = fork();
                //     if(id == 0)
                //     {
                //         //grandson
                //         server(sockfd, srcaddr); //孙子进程变成孤儿进程，被系统领养
                //         exit(0);
                //     }
                //     exit(0);
                // }

                // //pather
                // //父进程只关心_listensockfd，不关心sockdfd
                // close(sockfd); //建议
                // waitpid(pid, nullptr, 0);

                //------version 2------多线程（原生线程库）
                pthread_t t;
                ThreadData* td = new ThreadData(sockfd, srcaddr, this);
                pthread_create(&t, nullptr, pthreadServer, td);


                // // ------version 3------线程池
                // 线程池不适合这种耗时比较长的任务
                // task_t t = bind(&TcpServer::server, this, sockfd, srcaddr);
                // ThreadPool<task_t>::getInstance()->push(t);
            }
        }
        _isrunning = false;
    }

    void stopServer()
    {
        _isrunning = false;
    }

    ~TcpServer()
    {
    }

private:
    int _listensockfd; // 监听套接字描述符指针
    uint16_t _port;    // 端口号
    bool _isrunning;   // 服务器是否运行
};